<?php
class Pageley_Domain_BasicEntity implements Pageley_Domain_Entity {
    private $properties;
    private $type;
    private $id;
    private $propertyChangeListeners;
    private $validatedProperties;

    private static $logger;

    public function __construct($type) {
        $this->type = $type;
        $this->properties = new Pageley_Util_ArrayMap();
        foreach($type->getAttributes() as $propertyName => $attribute) {
            $this->properties->put($propertyName,
            $attribute->newProperty($this));
        }
        $this->validatedProperties = new Pageley_Util_ArrayMap();
    }

    private function getLogger() {
        $logger = self::$logger;
        if($logger === null) {
            $logFactory = Pageley_Log_Factory::getInstance();
            $logger = $logFactory->getLogger($this);
        }
        return $logger;
    }

    public function get($propertyName, $load = true) {
        $logger = $this->getLogger();
        if($logger->isTraceEnabled()) {
            $logger->trace("Getting '" . $this->type->getName() . "." . $propertyName . "'.");
        }
        $attribute = $this->type->getAttribute($propertyName);
        $value = null;
        $subpropertyName = null;
        if(Pageley_Util_String::contains($propertyName, ".")) {
            $subpropertyName = Pageley_Util_String::substringAfter($propertyName, ".");
            $propertyName = Pageley_Util_String::substringBefore($propertyName, ".");
        }
        $property = $this->properties->get($propertyName);
        if($subpropertyName !== null) {
            // TODO: also allow dot operator to browse through references
            $value = $property->getValue()->get($subpropertyName);
        } else {
            $value = $property->getValue($load);
        }
        return $value;
    }

    public function getProperty($propertyName) {
        return $this->properties->get($propertyName);
    }

    public function setProperty($propertyName, $property) {
        return $this->properties->put($propertyName, $property);
    }

    public function set($propertyName, $value, $validate = true) {
        $logger = $this->getLogger();
        if($logger->isTraceEnabled()) {
            $logger->trace("Setting '" . $this->type->getName() . "." . $propertyName . "' to " . ($value != null ? "'" . $value . "'" : "null") . ".");
        }
        $attribute = $this->type->getAttribute($propertyName);
        if($validate) {
            $attribute->validate($value, $propertyName, $this);
            $this->validatedProperties->put($propertyName, $attribute);
        }
        $property = $this->getProperty($propertyName);
        $oldValue = $property->getValue(false);
        $property->setValue($value);
        $this->notifyPropertyChangeListeners(new Pageley_Domain_PropertyChangedEvent(
            $this, $propertyName, $oldValue, $value));
    }

    public function getType() {
        return $this->type;
    }

    public function setId($id) {
        $this->id = $id;
    }

    public function getId() {
        return $this->id;
    }

    private function getPropertyChangeListeners() {
        if($this->propertyChangeListeners === null) {
            $this->propertyChangeListeners = new Pageley_Util_ArrayMap();
        }
        return $this->propertyChangeListeners;
    }

    private function notifyPropertyChangeListeners($event) {
        foreach($this->getPropertyChangeListeners() as $listener) {
            $listener->propertyChanged($event);
        }
    }

    public function addPropertyChangeListener($listener) {
        $this->getPropertyChangeListeners()->add($listener);
    }

    public function removePropertyChangeListener($listener) {
        $this->getPropertyChangeListeners()->removeValue($listener);
    }

    public function __toString() {
        return $this->type->getName() . " ( id = " . $this->id . ", properties = " . $this->properties . " )";
    }

    public function equals($entity) {
        $result = false;
        if($entity !== null
                && ($entity instanceof Pageley_Domain_BasicEntity)
                && $this->type->equals($entity->getType())) {
            $result = ($this->id !== null && $this->id === $entity->getId());
        }
        return $result;
    }

    public function compareTo($object) {
        return $this->id > $object->getId() ? 1 :
            ($this->id < $object->getId() ? -1 : 0);
    }
    
    public function validate() {
    	$errors = new Pageley_Util_ArrayMap();
        foreach($this->type->getAttributes() as $propertyName => $attribute) {
            if(!$this->validatedProperties->containsKey($propertyName)) {
                try {
                    $attribute->validate($this->get($propertyName, false), $propertyName, $this);
                } catch(Pageley_Domain_Validator_Exception $e) {
        			if(Pageley_Log::isDebugEnabled($this)) {
        				Pageley_Log::debug($this, "Validation of property '" . $propertyName . "' failed: " . $e->getErrors());
        			}
        			$errors->putAll($e->getErrors());
        		}
            }
        }
        if($errors->size() > 0) {
        	throw new Pageley_Domain_Validator_Exception($errors);
        }
    }
}